/*
 * Copyright (c) [2021] Huawei Technologies Co.,Ltd.All rights reserved.
 *
 * OpenArkCompiler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "safety_warning.h"
#include "global_tables.h"
#include "inline.h"
#include "me_dominance.h"
#include "me_irmap_build.h"
#include "mpl_logging.h"
#include "opcode_info.h"
#include <sstream>

namespace maple {
static std::string GetNthStr(size_t index) {
  switch (index) {
    case 0:
      return "1st";
    case 1:
      return "2nd";
    case 2:
      return "3rd";
    default: {
      std::ostringstream oss;
      oss << index << "th";
      return oss.str();
    }
  }
}

inline static void HandleAssertNonnull(const MeStmt &stmt, const MIRModule &mod) {
  auto srcPosition = stmt.GetSrcPosition();
  WARN(kLncWarn, "%s:%d warning: Dereference of nullable pointer",
       mod.GetFileNameFromFileNum(srcPosition.FileNum()).c_str(), srcPosition.LineNum());
}

inline static void HandleReturnAssertNonnull(const MeStmt &stmt, const MIRModule &mod, const MeFunction &func) {
  auto srcPosition = stmt.GetSrcPosition();
  WARN(kLncWarn, "%s:%d warning: %s return nonnull but got nullable pointer",
       mod.GetFileNameFromFileNum(srcPosition.FileNum()).c_str(), srcPosition.LineNum(), func.GetName().c_str());
}

inline static void HandleAssignAssertNonnull(const MeStmt &stmt, const MIRModule &mod) {
  auto srcPosition = stmt.GetSrcPosition();
  WARN(kLncWarn, "%s:%d warning: nullable assignment of nonnull pointer",
       mod.GetFileNameFromFileNum(srcPosition.FileNum()).c_str(), srcPosition.LineNum());
}

inline static void HandleCallAssertNonnull(const MeStmt &stmt, const MIRModule &mod) {
  auto srcPosition = stmt.GetSrcPosition();
  auto &callStmt = static_cast<const CallAssertNonnullMeStmt&>(stmt);
  WARN(kLncWarn, "%s:%d warning: nullable pointer passed to %s that requires nonnull for %s argument",
       mod.GetFileNameFromFileNum(srcPosition.FileNum()).c_str(), srcPosition.LineNum(), callStmt.GetFuncName().c_str(),
       GetNthStr(callStmt.GetParamIndex()).c_str());
}

inline static void HandleBoundaryCheckAssert(const MeStmt &stmt, const MIRModule &mod) {
  auto srcPosition = stmt.GetSrcPosition();
  WARN(kLncWarn, "%s:%d warning: cant't prove the offset is within the boundary",
       mod.GetFileNameFromFileNum(srcPosition.FileNum()).c_str(), srcPosition.LineNum());
}

inline static void HandleBoundaryCheckAssertAssign(const MeStmt &stmt, const MIRModule &mod) {
  auto srcPosition = stmt.GetSrcPosition();
  std::string msgFormat;
  WARN(kLncWarn, "%s:%d warning: cant't prove l-value's bounds is within r-value's bounds",
       mod.GetFileNameFromFileNum(srcPosition.FileNum()).c_str(), srcPosition.LineNum());
}

inline static void HandleBoundaryCheckAssertCall(const MeStmt &stmt, const MIRModule &mod) {
  auto srcPosition = stmt.GetSrcPosition();
  auto &callStmt = static_cast<const CallAssertBoundaryMeStmt&>(stmt);
  WARN(kLncWarn, "%s:%d warning: can't prove pointer's bounds match the function declaration for the %s argument",
       mod.GetFileNameFromFileNum(srcPosition.FileNum()).c_str(), srcPosition.LineNum(), callStmt.GetFuncName().c_str(),
       GetNthStr(callStmt.GetParamIndex()).c_str());
}

inline static void HandleBoundaryCheckAssertReturn(const MeStmt &stmt, const MIRModule &mod, const MeFunction &func) {
  auto srcPosition = stmt.GetSrcPosition();
  WARN(kLncWarn, "%s:%d warning: can't prove return value's bounds match the function declaration for %s",
       mod.GetFileNameFromFileNum(srcPosition.FileNum()).c_str(), srcPosition.LineNum(), func.GetName().c_str());
}

void MESafetyWarning::GetAnalysisDependence(maple::AnalysisDep &aDep) const {
  aDep.AddRequired<MEDominance>();
  aDep.AddRequired<MEIRMapBuild>();
  aDep.SetPreservedAll();
}

bool MESafetyWarning::PhaseRun(MeFunction &meFunction) {
  auto *dom = GET_ANALYSIS(MEDominance, meFunction);
  auto &mod = meFunction.GetMIRModule();
  auto fileInfos = mod.GetSrcFileInfo();
  for (auto *bb : dom->GetReversePostOrder()) {
    for (auto &stmt : bb->GetMeStmts()) {
      switch (stmt.GetOp()) {
        case OP_assertnonnull:
          HandleAssertNonnull(stmt, mod);
          break;
        case OP_returnassertnonnull:
          HandleReturnAssertNonnull(stmt, mod, meFunction);
          break;
        case OP_assignassertnonnull:
          HandleAssignAssertNonnull(stmt, mod);
          break;
        case OP_callassertnonnull:
          HandleCallAssertNonnull(stmt, mod);
          break;
        case OP_assertlt:
        case OP_assertge:
          HandleBoundaryCheckAssert(stmt, mod);
          break;
        case OP_assignassertlt:
        case OP_assignassertge:
          HandleBoundaryCheckAssertAssign(stmt, mod);
          break;
        case OP_callassertlt:
        case OP_callassertge:
          HandleBoundaryCheckAssertCall(stmt, mod);
          break;
        case OP_returnassertlt:
        case OP_returnassertge:
          HandleBoundaryCheckAssertReturn(stmt, mod, meFunction);
          break;
        default:
          break;
      }
    }
  }
  return true;
}
}  // namespace maple
